#-------------------------------------------------------------------------------
# Name:        Dialog
# Purpose:
#
# Author:      Michael Blackwood
#
# Created:     27/11/2013
# Copyright:   (c) Monxcleyr Productions 2013
# Licence:     GPL v3
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import pygame, logging
from ..gui import label
import config

class Dialog():

  """class comment"""

  def __init__(self, common, imagehandler):

    self.imagehandler = imagehandler

    self.conf = self.conf = config.Config()
    self.conf.read('resources/configs/dialogs.ini')
    # Get teh character used for new frames
    self.newframe = self.conf.get('General', 'newframe')
    self.switch = self.conf.get('General', 'switch')
    # Load the % style formatting
    formats = self.conf.items('Formatting')
    self.common = common
    self.formats = {}

    for pair in formats:
      self.formats[pair[0].upper()] = pair[1]

    self.themes = {}
    self.script = {}
    self.dialogs = []
    self.themeclasses = {}
    self.themeclasses['base'] = BaseTheme
    self.loadThemes()


  def loadThemes(self):

    self.conf = self.conf = config.Config()

    self.conf.read('resources/configs/dialogs.ini')

    # Find number of themes
    checking = 1
    count = 1
    while checking == 1:
      if self.conf.has_section("Theme_" + str(count)) == True:
        count += 1
      else:
        checking = 0

    # Load themes
    count -= 1
    for i in range(0,count):

      section = 'Theme_' + str(i + 1)
      attributes = {}

      attributes['type'] = self.conf.get(section, 'type')
      attributes['newframe'] = self.newframe
      attributes['switch'] = self.switch
      attributes['name'] = self.conf.get(section, 'name')
      attributes['type'] = self.conf.get(section, 'type')
      fontfile = 'resources/fonts/' + self.conf.get(section, 'font')
      attributes['fontcolor'] = self.conf.getint_tuple(section, 'fontcolor')
      attributes['fontsize'] = self.conf.getint(section, 'fontsize')
      attributes['font'] = (fontfile, attributes['fontsize'])
      attributes['basepos'] = self.conf.getint_tuple(section, 'basepos')
      attributes['textpos'] = self.conf.getint_tuple(section, 'textpos')
      attributes['width'] = self.conf.getint(section, 'width')
      attributes['linecount'] = self.conf.getint(section, 'linecount')
      attributes['bgimage'] = 'resources/graphics/dialogs/' + self.conf.get(section, 'bgimage')
      attributes['continuesound'] = self.conf.get(section, 'continuesound')
      if attributes['continuesound'] == 'None':
        attributes['continuesound'] = None

      attributes['arrowimage'] = None
      attributes['arrowpos'] = None
      if self.conf.has_option(section, 'arrowimage'):
        if self.conf.get(section, 'arrowimage') != 'None':
          attributes['arrowimage'] = 'resources/graphics/dialogs/' + self.conf.get(section, 'arrowimage')
          attributes['arrowpos'] = self.conf.getint_tuple(section, 'arrowpos')

      attributes['closeimage'] = None
      attributes['closeimage'] = None
      attributes['hoversound'] = None
      attributes['clicksound'] = None
      if self.conf.has_option(section, 'closeimage'):
        if self.conf.get(section, 'closeimage') != 'None':
          attributes['closeimage'] = 'resources/graphics/dialogs/' + self.conf.get(section, 'closeimage')
          attributes['closepos'] = self.conf.getint_tuple(section, 'closepos')
          if self.conf.get(section, 'hoversound') != 'None':
            attributes['hoversound'] = self.conf.get(section, 'hoversound')
          if self.conf.get(section, 'clicksound') != 'None':
            attributes['clicksound'] = self.conf.get(section, 'clicksound')

      self.themes[attributes['name']] = attributes
      logging.info('Loaded dialog theme "%s"' % attributes['name'])

#-------------------------------------------------------------------------------

  def loadScript(self, script='resources/scripts/dialogs.ini'):

    self.conf = config.Config()

    self.conf.read(script)

    sections = self.conf.sections()

    dialogs = []

    # throw the contents of each task into a dict to be processed later
    for dialog in sections:
      contents = dict(self.conf.items(dialog))
      contents['name'] = dialog
      dialogs.append(contents)


    for dialog in dialogs:
      dialog['active'] = dialog['active'] == 'True'
      dialog['closeable'] = dialog['closeable'] == 'True'
      dialog['time'] = self.convertTime(dialog['time'])

      pos = dialog['position']
      pos = pos.replace(' ', '')
      pos = pos.split(',')
      temp = []
      for i in pos:
        temp.append(int(i))
      dialog['position'] = temp

      modes = dialog['modes']
      modes = modes.replace(' ', '')
      modes = modes.split(',')
      temp = []
      for i in modes:
        temp.append(int(i))
      dialog['modes'] = temp

      self.script[dialog['name']] = dialog


    for key in self.script:
      self.addDialog(self.script[key]['theme'], ' ', self.script[key]['position'], self.script[key]['modes'], \
                            self.script[key]['name'], False, self.script[key]['closeable'],)

      self.getDialogByName(self.script[key]['name']).loadText(self.script[key]['text'])



  def convertTime(self, time):

    """ Convert a string time in 00:00 format to integer seconds """

    split = time.split(':')

    converted = 0
    #check that there was actually a minute supplied
    if split[0]:
      converted = int(split[0]) * 60

    converted += int(split[1])

    return converted




#-------------------------------------------------------------------------------

  def update(self, time):

    for key in self.script:
      if self.script[key]['time'] == time:
        dialog = self.getDialogByName(self.script[key]['name'])
        if self.script[key]['active'] == True and dialog.activated == False:
          dialog.setActive(True)



  def getDialogByName(self, name):

    for dialog in self.dialogs:
      if dialog.name == name:
        return dialog


  def addDialog(self, theme, text, position, modes, name, active=True, closeable=True):

    self.dialogs.append(self.createDialog(theme, text, position, modes, name, active, closeable))


  def draw(self, surface, mouse, sounds, common, mode=1):

    """ Draw any dialogs that are in the given mode. Default mode = 1 (Game) """

    for dialog in self.dialogs:
      if mode in dialog.modes and dialog.active:
        drew = True
        dialog.draw(surface, mouse, common, sounds, self)

  def checkActive(self, mode):

    for dialog in self.dialogs:
      if mode in dialog.modes and dialog.active:
        return True

    return False

  def createDialog(self, theme, text, position, modes, name, active, closeable):

    if self.themes[theme]['type'] in self.themeclasses:
      dialog = self.themeclasses[self.themes[theme]['type']](self.themes[theme], self.formats, self.common, self.imagehandler)
      dialog.text = text
      dialog.position = position
      dialog.modes = modes
      dialog.active = active
      dialog.closeable = closeable
      dialog.name = name
      logging.info('Initialized dialog instance "%s"' % dialog.name)

      return dialog
    else:
      logging.critical('Failed to create dialog with type "%s"' % self.themes[theme]['type'])
      return None


  def updateDialogPositions(self, mode):

    dialogs = []

    for dialog in self.dialogs:
      if mode in dialog.modes and dialog.active:
        dialogs.append(dialog)




#-------------------------------------------------------------------------------
#--------------------------------- THEMES --------------------------------------
#-------------------------------------------------------------------------------

class BaseTheme():

  def __init__(self, attributes, formats, common, imagehandler):
    self.common = common
    self.attributes = attributes
    self.imagehandler = imagehandler
    self.name = 'BaseTheme'
    self.active = False
    self.position = (100,100)
    self.arrowcount = 0
    self.loadFrames()
    self.modes = []
    self.closeprevhover = False
    self.frameindex = 0
    self.activated = False

    self.formats = formats

    self.textchanged = True
    self.frames = []

  def setActive(self, state):

    self.active = state
    self.activated = True


  def draw(self, surface, mouse, common, sounds, handler):

    font = common.fonthandler.getFontByTuple(self.attributes['font'])

    linelabel = label.Label(None, ' ', (0,0), 'left', font, self.attributes['fontcolor'])

    # check if confirm was clicked and move frame
    if self.attributes['closeimage'] == None:
      if common.keyState.check(common.key.bind['confirm'])[0] == True:
        self.frameindex += 1

    # Rewrap text if it has changed, usually just set once
    if self.textchanged:
      self.frames = self.wrap(self.text, self.attributes['width'], common)
      self.textchanged = False

    # Check if we forced a new index that is too high
    if self.frameindex + 1 > len(self.frames):
      self.active = False
      return None

    if self.attributes['switch'] in self.frames[self.frameindex][0]:
      name = self.frames[self.frameindex][0][1:]
      if handler.getDialogByName(name) == None:
        raise DialogException('No dialog to activate with name "%s' % name)
      # reset confirm key, so the game doesn't think you are confirming the next dialog
      common.keyState[self.common.key.bind['confirm']][0] = False
      handler.getDialogByName(name).setActive(True)
      handler.getDialogByName(name).active
      self.frameindex += 1
      return None



    # Draw bg and find position
    linelabel.screen = surface
    position = (self.position[0] - self.attributes['basepos'][0], self.position[1] - self.attributes['basepos'][1])
    linelabel.pos = (position[0] + self.attributes['textpos'][0], position[1] + self.attributes['textpos'][1])

    surface.blit(self.imagehandler.getImage(self.attributes['bgimage']), position)

    # Draw Lavels
    for line in self.frames[self.frameindex]:
      linelabel.setText(line)
      linelabel.draw()
      linelabel.pos = (linelabel.pos[0], linelabel.pos[1] + common.fonthandler.getFontByTuple(self.attributes['font']).get_linesize() + 1)

    # Draw arrow
    if self.attributes['arrowimage'] != None:
      self.drawArrow(surface, position)

    # Draw close
    if self.attributes['closeimage'] != None:
      self.drawClose(surface, position, mouse, sounds)


  def loadFrames(self):

    """ Load frames for arrows and buttons. """

    # Load frames for arrow

    if self.attributes['arrowimage'] != None:
      self.attributes['arrowframes'] = []

      size = self.imagehandler.getImage(self.attributes['arrowimage']).get_size()

      height = size[1] / 5

      for i in range(0,5):
        self.attributes['arrowframes'].append(pygame.rect.Rect((0, height * i), (size[0], height)))

    # Load frames for close button
    if self.attributes['closeimage'] != None:
      self.attributes['closeframes'] = []

      size = self.imagehandler.getImage(self.attributes['closeimage']).get_size()

      height = size[1] / 3

      for i in range(0, 5):
        self.attributes['closeframes'].append(pygame.rect.Rect((0, height * i), (size[0], height)))



  def drawArrow(self, surface, position):

    self.arrowcount += 1
    if self.arrowcount > 49:
      self.arrowcount = 0

    frame = self.arrowcount / 10

    pos = (self.attributes['arrowpos'][0] + position[0], self.attributes['arrowpos'][1] + position[1])

    surface.blit(self.imagehandler.getImage(self.attributes['arrowimage']), pos, self.attributes['arrowframes'][frame])


  def drawClose(self, surface, position, mouse, sounds):

    pos = (self.attributes['closepos'][0] + position[0], self.attributes['closepos'][1] + position[1])

    rect = pygame.rect.Rect(self.attributes['closeframes'][0])
    rect.topleft = pos

    frame = 0
    if rect.collidepoint(mouse[0]):
      frame = 1
      if self.closeprevhover == False:
        sounds.play(self.attributes['hoversound'])
        self.closeprevhover = True
    else:
      self.closeprevhover = False
    if rect.collidepoint(mouse[0]) and mouse[1] == 1:
      frame = 2
      self.active = False
      sounds.play(self.attributes['clicksound'])

    surface.blit(self.imagehandler.getImage(self.attributes['closeimage']), pos, self.attributes['closeframes'][frame])


  def loadText(self, dialogfile):

    """ Load text from given file. Root: 'resources/dialogs/'

    Dialog will rewrap text next frame. """

    with open('resources/dialogs/' + dialogfile) as f:

      self.text = f.read()
      self.textchanged = True


  def formatText(self, text):

    for var in self.formats:
      if self.formats[var] in self.attributes:
        text = text.replace(var, self.attributes[self.formats[var]])

    text = text.replace('%90', "90's tote bag literally next level.")
    text = text.replace('%TCC', "TheChosenChaser")
    text = text.replace('%PS', "Pablo, of #PABLOSERIES fame.")

    return text



  def wrap(self, text, size, common):

    """ Wrap text to fit given width (size).
    Will break into an array of lines, divided by frames. """

    replaced = self.formatText(text)

    textnewlines = replaced.splitlines()


    # breaking up the text into individual words with trailing spaces
    textnew = []
    for line in textnewlines:
      textsplit = line.split(' ')
      temp = []
      for word in textsplit:
        temp.append(word + ' ')
      textnew.append(temp)

    lines = []
    index = 0


    for requested_line in textnewlines:
      if common.fonthandler.getFontByTuple(self.attributes['font']).size(requested_line)[0] > size:
        words = requested_line.split(' ')
        # if any of our words are too long to fit, return.
        for word in words:
          if common.fonthandler.getFontByTuple(self.attributes['font']).size(word)[0] >= size:
            raise DialogException, "The word " + word + " is too long for dialog of width " + str(size) + '.'
        # Start a new line
        accumulated_line = ""
        for word in words:
          test_line = accumulated_line + word + " "
          # Build the line while the words fit.
          if common.fonthandler.getFontByTuple(self.attributes['font']).size(test_line)[0] < size:
            accumulated_line = test_line
          else:
            lines.append(accumulated_line)
            accumulated_line = word + " "
        lines.append(accumulated_line)
      else:
        if requested_line == '':
          lines.append(' ')
        else:
          lines.append(requested_line)

##    print lines

    # Break text up into frames
    frames = []
    while len(lines) != 0:
      frame = [' '] * self.attributes['linecount']
      checking = True
      count = 0
      while checking and len(lines) != 0:

        if count + 1 == len(frame):
          checking = False

        # switch commands get their own frame
        if self.attributes['switch'] in lines[0]:
          if count == 0:
            frame[count] = lines[0]
            checking = False
            lines.pop(0)
            if len(lines) > 0:
              if self.attributes['newframe'] in lines[0]:
                checking = False
                lines.pop(0)
          else:
            checking = False
          break

        if self.attributes['newframe'] in lines[0]:
          checking = False
          lines.pop(0)
        else:
          frame[count] = lines[0]
          lines.pop(0)

        count += 1

      frames.append(frame)



##    print frames
    return frames





class DialogException(Exception):
  """ :( """
  def __init__(self, message = None):
    self.message = message
  def __str__(self):
    return self.message


